///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef _Texture_H__
#define _Texture_H__

#include "OgrePrerequisites.h"
#include "OgreHardwareBuffer.h"
#include "OgreResource.h"
#include "OgreImage.h"

namespace Ogre {

	
	
	
    enum TextureUsage
    {
		/// @copydoc HardwareBuffer::Usage
		TU_STATIC = HardwareBuffer::HBU_STATIC,
		TU_DYNAMIC = HardwareBuffer::HBU_DYNAMIC,
		TU_WRITE_ONLY = HardwareBuffer::HBU_WRITE_ONLY,
		TU_STATIC_WRITE_ONLY = HardwareBuffer::HBU_STATIC_WRITE_ONLY, 
		TU_DYNAMIC_WRITE_ONLY = HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY,
		TU_DYNAMIC_WRITE_ONLY_DISCARDABLE = HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE,
		/// mipmaps will be automatically generated for this texture
		TU_AUTOMIPMAP = 0x100,
		/// this texture will be a render target, i.e. used as a target for render to texture
		/// setting this flag will ignore all other texture usages except TU_AUTOMIPMAP
		TU_RENDERTARGET = 0x200,
		/// default to automatic mipmap generation static textures
		TU_DEFAULT = TU_AUTOMIPMAP | TU_STATIC_WRITE_ONLY
        
    };

    
    enum TextureType
    {
        /// 1D texture, used in combination with 1D texture coordinates
        TEX_TYPE_1D = 1,
        /// 2D texture, used in combination with 2D texture coordinates (default)
        TEX_TYPE_2D = 2,
        /// 3D volume texture, used in combination with 3D texture coordinates
        TEX_TYPE_3D = 3,
        /// 3D cube map, used in combination with 3D texture coordinates
        TEX_TYPE_CUBE_MAP = 4,
        /// 2D texture array
        TEX_TYPE_2D_ARRAY = 5
    };

	
	enum TextureMipmap
	{
		/// Generate mipmaps up to 1x1
		MIP_UNLIMITED = 0x7FFFFFFF,
		/// Use TextureManager default
		MIP_DEFAULT = -1
	};

    // Forward declaration
    class TexturePtr;

    
    class _OgreExport Texture : public Resource
    {
    public:
        Texture(ResourceManager* creator, const String& name, ResourceHandle handle,
            const String& group, bool isManual = false, ManualResourceLoader* loader = 0);

        
        virtual void setTextureType(TextureType ttype ) { mTextureType = ttype; }

        
        virtual TextureType getTextureType(void) const { return mTextureType; }

        
        virtual size_t getNumMipmaps(void) const {return mNumMipmaps;}

		
        virtual void setNumMipmaps(size_t num) {mNumRequestedMipmaps = mNumMipmaps = num;}

		
		virtual bool getMipmapsHardwareGenerated(void) const { return mMipmapsHardwareGenerated; }

        
        virtual float getGamma(void) const { return mGamma; }

        
        virtual void setGamma(float g) { mGamma = g; }

		
		virtual void setHardwareGammaEnabled(bool enabled) { mHwGamma = enabled; }

		
		virtual bool isHardwareGammaEnabled() const { return mHwGamma; }

		
		virtual void setFSAA(uint fsaa, const String& fsaaHint) { mFSAA = fsaa; mFSAAHint = fsaaHint; }

		
		virtual uint getFSAA() const { return mFSAA; }

		
		virtual const String& getFSAAHint() const { return mFSAAHint; }

		
        virtual size_t getHeight(void) const { return mHeight; }

        
        virtual size_t getWidth(void) const { return mWidth; }

        
        virtual size_t getDepth(void) const { return mDepth; }

        
        virtual size_t getSrcHeight(void) const { return mSrcHeight; }

        
        virtual size_t getSrcWidth(void) const { return mSrcWidth; }

        
        virtual size_t getSrcDepth(void) const { return mSrcDepth; }

        
        virtual void setHeight(size_t h) { mHeight = mSrcHeight = h; }

        
        virtual void setWidth(size_t w) { mWidth = mSrcWidth = w; }

        
        virtual void setDepth(size_t d)  { mDepth = mSrcDepth = d; }

        
        virtual int getUsage() const
        {
            return mUsage;
        }

        
        virtual void setUsage(int u) { mUsage = u; }

        
        virtual void createInternalResources(void);

        
        virtual void freeInternalResources(void);
        
		
		virtual void copyToTexture( TexturePtr& target );

        
        virtual void loadImage( const Image &img );
			
		
		virtual void loadRawData( DataStreamPtr& stream, 
			ushort uWidth, ushort uHeight, PixelFormat eFormat);

		
        virtual void _loadImages( const ConstImagePtrList& images );

		
		virtual PixelFormat getFormat() const
		{
			return mFormat;
		}

        
        virtual PixelFormat getDesiredFormat(void) const
        {
            return mDesiredFormat;
        }

        
        virtual PixelFormat getSrcFormat(void) const
        {
            return mSrcFormat;
        }

        
        virtual void setFormat(PixelFormat pf);

        
        virtual bool hasAlpha(void) const;

        
        virtual void setDesiredIntegerBitDepth(ushort bits);

        
        virtual ushort getDesiredIntegerBitDepth(void) const;

        
        virtual void setDesiredFloatBitDepth(ushort bits);

        
        virtual ushort getDesiredFloatBitDepth(void) const;

        
        virtual void setDesiredBitDepths(ushort integerBits, ushort floatBits);

        
        virtual void setTreatLuminanceAsAlpha(bool asAlpha);

        
        virtual bool getTreatLuminanceAsAlpha(void) const;

        
        virtual size_t getNumFaces() const;

		
		virtual HardwarePixelBufferSharedPtr getBuffer(size_t face=0, size_t mipmap=0) = 0;


		
		virtual void convertToImage(Image& destImage, bool includeMipMaps = false);
		
		
		virtual void getCustomAttribute(const String& name, void* pData) {}
		


    protected:
        size_t mHeight;
        size_t mWidth;
        size_t mDepth;

        size_t mNumRequestedMipmaps;
		size_t mNumMipmaps;
		bool mMipmapsHardwareGenerated;
        float mGamma;
		bool mHwGamma;
		uint mFSAA;
		String mFSAAHint;

        TextureType mTextureType;
		PixelFormat mFormat;
        int mUsage; // Bit field, so this can't be TextureUsage

        PixelFormat mSrcFormat;
        size_t mSrcWidth, mSrcHeight, mSrcDepth;

        PixelFormat mDesiredFormat;
        unsigned short mDesiredIntegerBitDepth;
        unsigned short mDesiredFloatBitDepth;
        bool mTreatLuminanceAsAlpha;

		bool mInternalResourcesCreated;

		/// @copydoc Resource::calculateSize
		size_t calculateSize(void) const;
		

		
		virtual void createInternalResourcesImpl(void) = 0;

		
		virtual void freeInternalResourcesImpl(void) = 0;

		
		void unloadImpl(void);

		
		String getSourceFileType() const;

    };

    
    class _OgreExport TexturePtr : public SharedPtr<Texture> 
    {
    public:
        TexturePtr() : SharedPtr<Texture>() {}
        explicit TexturePtr(Texture* rep) : SharedPtr<Texture>(rep) {}
        TexturePtr(const TexturePtr& r) : SharedPtr<Texture>(r) {} 
        TexturePtr(const ResourcePtr& r) : SharedPtr<Texture>()
        {
			// lock & copy other mutex pointer
            OGRE_MUTEX_CONDITIONAL(r.OGRE_AUTO_MUTEX_NAME)
            {
			    OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
			    OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
                pRep = static_cast<Texture*>(r.getPointer());
                pUseCount = r.useCountPointer();
                if (pUseCount)
                {
                    ++(*pUseCount);
                }
            }
        }

        /// Operator used to convert a ResourcePtr to a TexturePtr
        TexturePtr& operator=(const ResourcePtr& r)
        {
            if (pRep == static_cast<Texture*>(r.getPointer()))
                return *this;
            release();
			// lock & copy other mutex pointer
            OGRE_MUTEX_CONDITIONAL(r.OGRE_AUTO_MUTEX_NAME)
            {
			    OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
			    OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
                pRep = static_cast<Texture*>(r.getPointer());
                pUseCount = r.useCountPointer();
                if (pUseCount)
                {
                    ++(*pUseCount);
                }
            }
			else
			{
				// RHS must be a null pointer
				assert(r.isNull() && "RHS must be null if it has no mutex!");
				setNull();
			}
            return *this;
        }
    };
	
	

}

#endif
